<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="ggStL" id="ggStL"><span data-lake-id="u0430c86b" id="u0430c86b">典型回答</span></h1>
  <p data-lake-id="uc43802ed" id="uc43802ed"><br></p>
  <p data-lake-id="u913cdef6" id="u913cdef6"><span data-lake-id="uca7129fd" id="uca7129fd">从Java虚拟机的角度来讲，只存在两种不同的类加载器：一种是启动类加载器（Bootstrap ClassLoader），这个类加载器使用C++语言实现（只限于HotSpot），是虚拟机自身的一部分；</span></p>
  <p data-lake-id="ue0231b04" id="ue0231b04"><span data-lake-id="u9766b638" id="u9766b638">​</span><br></p>
  <p data-lake-id="u190ccbb2" id="u190ccbb2"><span data-lake-id="uc1ab5637" id="uc1ab5637">另一种就是所有其他的类加载器，这些类加载器都由Java语言实现，独立于虚拟机外部，并且全都继承自抽象类java.lang.ClassLoader。</span></p>
  <p data-lake-id="ub7e7476d" id="ub7e7476d"><span data-lake-id="u98e521d2" id="u98e521d2">​</span><br></p>
  <p data-lake-id="u593b6d12" id="u593b6d12"><span data-lake-id="ue3fa3b50" id="ue3fa3b50">从Java开发人员的角度来看，类加载器还可以划分得更细致一些，不过，这里需要区分JDK的版本，在JDK 1.8及之前的版本，和之后的版本是不太一样的。主要是因为JDK 1.9中提供了Jigsaw实现模块化，导致类加载器发生了一些变化。</span></p>
  <p data-lake-id="ua929a021" id="ua929a021"><span data-lake-id="u52ee04cb" id="u52ee04cb">​</span><br></p>
  <p data-lake-id="u0d9dc95e" id="u0d9dc95e"><span data-lake-id="uf9e54f36" id="uf9e54f36">在JDK1.9中，原来的扩展类加载器被重命名为</span><strong><span data-lake-id="ucf875ba1" id="ucf875ba1">平台类加载器—</span></strong><strong><span data-lake-id="ua5f60fc0" id="ua5f60fc0" style="color: var(--tw-prose-code)">PlatformClassLoader</span></strong><span data-lake-id="uc3990e00" id="uc3990e00" class="lake-fontsize-12" style="color: rgb(55, 65, 81)"> </span><span data-lake-id="u96a79618" id="u96a79618">。它主要加载的是那些属于Java平台模块系统的非核心模块。这些模块在 module-info.java 文件中被明确声明。</span></p>
  <p data-lake-id="u7cbca34f" id="u7cbca34f"><span data-lake-id="ub8cbc02f" id="ub8cbc02f">​</span><br></p>
  <h3 data-lake-id="Y6HPv" id="Y6HPv"><span data-lake-id="ua94370e2" id="ua94370e2">JDK 1.8及之前</span></h3>
  <p data-lake-id="u36769aaa" id="u36769aaa"><span data-lake-id="u1b1824ca" id="u1b1824ca">​</span><br></p>
  <p data-lake-id="u4d385c96" id="u4d385c96"><span data-lake-id="u3575efef" id="u3575efef">JDK 1.8 以前会使用到以下3种系统提供的类加载器。</span></p>
  <p data-lake-id="u0bcd44de" id="u0bcd44de"><span data-lake-id="ue28f231f" id="ue28f231f">​</span><br></p>
  <ol list="u571473dd">
   <li fid="udb5969ee" data-lake-id="ua0367445" id="ua0367445"><strong><span data-lake-id="ucfa0014e" id="ucfa0014e">启动类加载器（Bootstrap ClassLoader</span></strong><span data-lake-id="u03dba72a" id="u03dba72a">）： 这个类加载器负责将存放在＜JAVA_HOME＞\lib目录中的，或者被-Xbootclasspath参数所指定的路径中的，并且是虚拟机识别的（仅按照文件名识别，如rt.jar，名字不符合的类库即使放在lib目录中也不会被加载）类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用，用户在编写自定义类加载器时，如果需要把加载请求委派给引导类加载器，那直接使用null代替即可</span></li>
   <li fid="udb5969ee" data-lake-id="uf05bd75a" id="uf05bd75a"><strong><span data-lake-id="u4b341cd5" id="u4b341cd5">扩展类加载器（Extension ClassLoader）</span></strong><span data-lake-id="uab1d6c9c" id="uab1d6c9c">： 这个加载器由sun.misc.Launcher$ExtClassLoader实现，它负责加载＜JAVA_HOME＞\lib\ext目录中的，或者被java.ext.dirs系统变量所指定的路径中的所有类库，开发者可以直接使用扩展类加载器。</span></li>
   <li fid="udb5969ee" data-lake-id="u1c3f4dd5" id="u1c3f4dd5"><strong><span data-lake-id="u55a92dd9" id="u55a92dd9">应用程序类加载器（Application ClassLoader）</span></strong><span data-lake-id="u613720b1" id="u613720b1">： 这个类加载器由sun.misc.Launcher$App-ClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值，所以一般也称它为系统类加载器。它负责加载用户类路径（ClassPath）上所指定的类库，开发者可以直接使用这个类加载器，如果应用程序中没有自定义过自己的类加载器，一般情况下这个就是程序中默认的类加载器。</span></li>
  </ol>
  <p data-lake-id="u8f904e39" id="u8f904e39"><span data-lake-id="ub75feb2d" id="ub75feb2d">​</span><br></p>
  <p data-lake-id="uf254c7b3" id="uf254c7b3"><img src="https://cdn.nlark.com/yuque/0/2024/png/5378072/1704516962330-42578c85-4180-4535-85ff-783e408d7764.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_26%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u864f6436" id="u864f6436"><br></p>
  <h3 data-lake-id="TmXaS" id="TmXaS"><span data-lake-id="u47d27583" id="u47d27583">JDK 1.9及以后</span></h3>
  <p data-lake-id="u90b90923" id="u90b90923"><br></p>
  <p data-lake-id="u5155276f" id="u5155276f"><span data-lake-id="u1e2535a8" id="u1e2535a8">在JDK 9中，类加载器发生了一些变化，原来的扩展类加载器被重命名为</span><strong><span data-lake-id="u4d455e7d" id="u4d455e7d">平台类加载器—</span></strong><strong><span data-lake-id="ufa5a0014" id="ufa5a0014" style="color: var(--tw-prose-code)">PlatformClassLoader</span></strong><span data-lake-id="uda1a68bd" id="uda1a68bd" class="lake-fontsize-12" style="color: rgb(55, 65, 81)"> </span><span data-lake-id="u33b24ae7" id="u33b24ae7">。</span></p>
  <p data-lake-id="uab6ed095" id="uab6ed095"><span data-lake-id="u8d3ccbdf" id="u8d3ccbdf">​</span><br></p>
  <p data-lake-id="u7468c27e" id="u7468c27e"><strong><span data-lake-id="u919a4bf2" id="u919a4bf2">PlatformClassLoader</span></strong><span data-lake-id="uc14b9d22" id="uc14b9d22"> 负责加载JDK平台本身的类库，这些类库位于JDK的 lib 目录下，但不包括核心的 java.* 类库（这些由启动类加载器加载）。它主要加载的是那些属于Java平台模块系统的非核心模块。这些模块在 module-info.java 文件中被明确声明。</span></p>
  <p data-lake-id="ufbcbcb8c" id="ufbcbcb8c"><span data-lake-id="u7844f958" id="u7844f958">​</span><br></p>
  <p data-lake-id="u2eb3d188" id="u2eb3d188"><img src="https://cdn.nlark.com/yuque/0/2024/png/5378072/1704518033721-2177d4ef-a79d-4b21-a980-fcac04264cde.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_25%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u9dc6b631" id="u9dc6b631"><br></p>
  <h1 data-lake-id="wmgwB" id="wmgwB"><span data-lake-id="u549b36a2" id="u549b36a2">扩展知识</span></h1>
  <p data-lake-id="u4717e866" id="u4717e866"><br></p>
  <h2 data-lake-id="h3tg3" id="h3tg3"><span data-lake-id="u97e7c664" id="u97e7c664">类加载器之间的关系</span></h2>
  <p data-lake-id="u1ffe1847" id="u1ffe1847"><br></p>
  <p data-lake-id="u7aa77d26" id="u7aa77d26"><span data-lake-id="u554349f0" id="u554349f0">很多人因为知道双亲委派这个词，就以为这些类加载器之间是继承关系，但是其实不是的。他们之间是组合关系。</span></p>
  <p data-lake-id="u7ab64afe" id="u7ab64afe"><span data-lake-id="uefd96d02" id="uefd96d02">​</span><br></p>
  <p data-lake-id="u052eb23a" id="u052eb23a"><br></p>
  <p data-lake-id="u97e316d2" id="u97e316d2"><span data-lake-id="uc8c675d5" id="uc8c675d5">在ClassLoader类中有一个ClassLoader类型的成员变量，他叫parent，他其实就是代表着当前类的上一层类加载器。</span></p>
  <p data-lake-id="ub1cddf6a" id="ub1cddf6a"><br></p>
  <pre lang="java"><code>
public abstract class ClassLoader {

    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final ClassLoader parent;
}
</code></pre>
  <p data-lake-id="ud89a835d" id="ud89a835d"><span data-lake-id="ue59398e2" id="ue59398e2">​</span><br></p>
  <p data-lake-id="u9bad46c8" id="u9bad46c8"><span data-lake-id="u4a314e53" id="u4a314e53">这就是通过组合而不是继承引入进来的，当我们想要把一个类委派给上层类加载加载时，直接调用parent.loadClass即可，如 </span><a href="https://github.com/zxiaofan/JDK/blob/master/JDK1.8/src/java/lang/ClassLoader.java#L356" target="_blank" data-lake-id="u3cdff01e" id="u3cdff01e"><span data-lake-id="ud386fed0" id="ud386fed0">代码</span></a><span data-lake-id="ue2e7facd" id="ue2e7facd">：</span></p>
  <p data-lake-id="ud82e977c" id="ud82e977c"><span data-lake-id="uc16adbf2" id="uc16adbf2">​</span><br></p>
  <p data-lake-id="u549d8be6" id="u549d8be6"><img src="https://cdn.nlark.com/yuque/0/2024/png/5378072/1704517868481-46d10fe6-533c-4b85-b072-33d25941e753.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_27%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
 </body>
</html>